package com.gitee.wsl.struct.grammar.parser.combinator

import arrow.core.NonEmptyList
import arrow.core.nonEmptyListOf
import com.gitee.wsl.struct.grammar.parser.ParsingScope
import com.gitee.wsl.struct.grammar.parser.result.ParseError
import com.gitee.wsl.struct.grammar.parser.result.ParseErrorContextProvider
import com.gitee.wsl.struct.grammar.parser.result.getOrElse
import com.gitee.wsl.struct.grammar.parser.token.AbstractToken
import com.gitee.wsl.struct.grammar.parser.token.AbstractWarpToken
import com.gitee.wsl.struct.grammar.parser.token.Token
import com.gitee.wsl.struct.grammar.parser.token.TokenMatch
import com.gitee.wsl.struct.grammar.util.toPrintableString
import com.gitee.wsl.transform.validator.ValidatorWithTransform
import com.gitee.wsl.transform.validator.string.StringValidator
import com.gitee.wsl.transform.validator.validateWithResult


/*fun <P:Token> P.validate(validatorWithError: ValidatorWithError<String?, String, *>.(ParsingScope)->ValidatorWithError<String?, String, *>) = apply {
    map{
        val baseVal = ValidatorWithError.notNull<String,Exception> { Exception("error") }.validatorWithError(this)
        baseVal.validate(it.text).onLeft {
            Timber.d("validate error $it")
            tryParse(FailToken(this@validate))
        }
    }
}*/

fun <P: AbstractToken> P.validate(validator: StringValidator): AbstractToken {
    return StringValidateToken(this@validate,validator)
}

fun <P: AbstractToken> P.validate(validatorWithTransform: ValidatorWithTransform<String?, String, *>.()->ValidatorWithTransform<String?, String, *>): AbstractToken {
      val baseVal = ValidatorWithTransform.notNull<String,Exception> { Exception("error") }.validatorWithTransform()
      return ValidateToken(this@validate,baseVal)
}

class ValidateToken<T: AbstractToken>(val token: T, val validatorWithTransform: ValidatorWithTransform<String?, String, *>): AbstractWarpToken(token)  {

    override suspend fun ParsingScope.parse(): TokenMatch {
        val ret = tryParse(token).getOrElse { fail(it) }
        val validator = validatorWithTransform(ret.text)
        validator.onLeft {
            fail(UnvalidatedToken(base,it,position))
        }
        return ret
    }
}

class StringValidateToken<T: AbstractToken>(val token: T, val validator: StringValidator): AbstractWarpToken(token)  {

    override suspend fun ParsingScope.parse(): TokenMatch {
        val ret = tryParse(token).getOrElse { fail(it) }
        validator.validateWithResult(ret.text).onFailure {
            fail(UnvalidatedToken(base, nonEmptyListOf(it.message?:""),position))
        }

        return ret
    }
}


data class UnvalidatedToken(
    val expected: Token,
    val expectedValidate: NonEmptyList<*>,
    override val offset: Int,
    override val contextProvider: ParseErrorContextProvider? = null
) : ParseError() {

    override fun toString(): String = describe()

    override fun describe(): String = format(
        message = "Unvalidated token at offset=$offset, when expected: ${expected.toPrintableString()} ,validate:${expectedValidate.toPrintableString()}",
        messageAtOffset = "Expected token: ${expected.toPrintableString()}"
    )
}